package com.mhs.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mhs.config.Authorization;
import com.mhs.entity.SysRole;
import com.mhs.entity.User;
import com.mhs.exception.BizException;
import com.mhs.mapper.SysRoleMapper;
import com.mhs.mapper.UserMapper;
import com.mhs.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mhs.utils.BaseContext;
import com.mhs.utils.JwtUtil;
import com.mhs.utils.RedisCache;
import com.mhs.vo.Jscode2session;
import com.mhs.vo.LoginUser;
import com.mhs.vo.Result;
import com.mhs.vo.UserVo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author Fantasy0521
 * @since 2023-04-01
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    private AuthenticationManager authenticationManager;
    
    @Resource
    private RedisCache redisCache; 
    
    @Resource
    private UserMapper userMapper;
    
    @Resource
    private SysRoleMapper sysRoleMapper;
    
    @Override
    public Boolean isAdmin(User user) {
        //1 判断是否存在该用户
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName,user.getUserName());
        user = this.getOne(queryWrapper);
        if (user == null) {
            return false;
        }
        //2 判断该用户的权限是否是管理员
        SysRole roleByuserId = userMapper.getUserRoleByuserId(user.getId());
        Authorization admin = Authorization.ADMIN;
        String role = admin.toString().toLowerCase();
        if (role.equals( roleByuserId.getRoleKey())){
            return true;
        }
        return false;
    }

    /**
     * 获取用户信息列表
     * @return
     * @param search
     * @param pageNum
     * @param pageSize
     */
    @Override
    public Result getUserList(String search, Integer pageNum, Integer pageSize) {
        //分页
        List<UserVo> list = userMapper.getUserList(search,(pageNum - 1) * pageSize,pageSize);
        Page<UserVo> page = new Page<>();
        page.setRecords(list);
        page.setTotal(list.size());
        return Result.success(page);
    }

    /**
     * 修改用户信息
     * @param userVo
     * @return
     */
    @Override
    public Result updateUser(UserVo userVo) {
        if ("admin".equals(userVo.getRole())){
            return Result.failure("不能将用户权限修改为管理员!");
        }
        // 修改用户信息
        this.updateById(userVo);

        if (userVo.getRole().isEmpty()) {
            return Result.success("更改用户信息成功");
        }
        
        //查询系统是否有该权限
        LambdaQueryWrapper<SysRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysRole::getRoleKey,userVo.getRole());
        Long roleId = sysRoleMapper.selectOne(queryWrapper).getId();
        if (roleId == null) {
            return Result.success("更改用户信息成功");
        }
        //修改权限信息
        sysRoleMapper.updateSysRoleUser(roleId,userVo.getId());

        return Result.success("更改用户信息成功");
    }

    @Override
    public Result login(User user) {
        //AuthenticationManager authenticate 进行认证
        //根据用户名密码进行验证 todo 根据手机号+验证码登陆
        UsernamePasswordAuthenticationToken authenticationToken = new
                UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        //如果认证没有通过,给出对应的提示
        //authenticate为null即为认证失败
        if (Objects.isNull(authenticate)) {
            throw new RuntimeException(new BizException("登陆失败"));
        }
        //如果认证通过了,使用userId生成一个JWT jwt存入Result返回
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        Long id = loginUser.getUser().getId();
        String jwt = JwtUtil.createJWT(id.toString());
        //把完整的用户信息存入redis userId作为key  token: jwt
        Map<String,Object> map = new HashMap<>();
        map.put("token",jwt);
        map.put("loginUser",loginUser.getUser());
        //获取当前角色的权限信息
        String role = loginUser.getPermissions().get(0);
        map.put("role",role);
        BaseContext.setCurrentId(id);// 将id存入tomcat当前线程,方便获取
        redisCache.setCacheObject("login:"+id.toString(),loginUser,1, TimeUnit.DAYS);
        return Result.success("登陆成功",map);
    }

    @Override
    public Result logout() {
        //获取SecurityContextHolder中的用户id
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long id = loginUser.getUser().getId();
        //删除redis中的值
        redisCache.deleteObject("login:"+id);
        return Result.success("退出登陆成功");
    }


    @Resource
    private RestTemplate restTemplate;

    @Value("${weixin.appId}")
    private String appId;

    @Value("${weixin.appSecret}")
    private String appSecret;

    /**
     * 微信登陆
     * @param code 临时凭证
     * @return 
     */
    @Override
    public Result loginByWeixin(String code,String avatarUrl,String nickName) {
        //官方api地址
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid="
                +appId+"&secret="+appSecret+"&js_code="+code+"&grant_type=authorization_code";
        //通过临时票据code获取session_key和openid
        String json = restTemplate.getForObject(url, String.class);
        //使用fastJSON转化为Java对象
        Jscode2session jscode2session = JSON.parseObject(json, Jscode2session.class);
        if (jscode2session == null || jscode2session.getOpenid() == null) {
            throw new RuntimeException(new BizException("用户临时票据已失效或已使用"));
        }
        //获取openId,并使用openId作为用户账号userName
        String openid = jscode2session.getOpenid();
        //查询数据库,判断是否存在此用户,存在则登陆,不存在则注册
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName,openid);
        User user = this.getOne(queryWrapper);
        if (user == null) {
            //注册账号
            user = new User();
            user.setUserName(openid);
            //密码默认为a,微信一键登陆也用不到密码
            user.setPassword("a");
            user.setAvatar(avatarUrl);
            user.setNickName(nickName);
            register(user,1);
        }
        //进行登陆认证,并返回token和用户信息
        user.setPassword("a");//这里必须设定为规定的默认密码,因为查出来的密码是加密过的无法直接进行认证
        return login(user);
    }

    @Resource
    private PasswordEncoder passwordEncoder;

    /**
     * 根据权限进行注册
     * @param user
     * @param roleId 权限 1: user 2: doctor 3: Admin
     * @return
     */
    @Override
    @Transactional
    public Result register(User user,Integer roleId) {
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        if (user.getNickName() == null) {
            //默认生成一个昵称
            String nickName = "用户" + UUID.randomUUID().toString().substring(0,6);
            user.setNickName(nickName);
        }
        //对密码进行加密后存入数据库
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        //插入用户表
        userMapper.insertUser(user);
        Long id = user.getId();
        //插入用户权限表
        userMapper.insertUserRole(id,roleId);
        return Result.success("注册成功");
    }

    /**
     * 账号密码注册或登陆
     * @param user
     * @return
     */
    @Override
    public Result registerOrLogin(User user) {
        if (user.getUserName().isEmpty() || user.getPassword().isEmpty()) {
            return Result.failure("账号或密码不能为空");
        }
        //查询用户是否已经注册
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName,user.getUserName());
        User one = this.getOne(queryWrapper);
        if (one != null) {
            //进行登陆验证
            return login(user);
        }
        //进行注册
        //因为密码会进行加密,所以先暂存一下加密前的密码,供下面登陆使用
        String password = user.getPassword();
        register(user,1);
        //注册完成后还要登陆一下获取token
        user.setPassword(password);
        return login(user);
    }


}
